/// <reference path='Scripts/typings/knockout/knockout.d.ts'/>
/// <reference path='AutomatonViewModel.ts'/>

module DMath {
	class BinarySelector {
		public isSelecting: boolean;
		public operation: string;
		public firstAutomaton: AutomatonViewModel;
		public secondAutomaton: AutomatonViewModel;

		constructor(operation: string) {
			this.operation = operation;
			this.isSelecting = true;
		}
	}

	export class GlobalViewModel {

		private static automatonContainerName: string = ".automaton-container";
		private static automatonVisualizerName: string = ".automaton-visualizer";
		private static wordRecognizerFieldName: string = ".word-input-field";
		private static self: GlobalViewModel;

		private dispatcher: any;
		private binarySelector: BinarySelector;
		
		public automata: KnockoutObservableArray<AutomatonViewModel>;

		public isZoomedIn: boolean = true;

		constructor() {
			GlobalViewModel.self = this;
			this.automata = ko.observableArray<AutomatonViewModel>();
			this.dispatcher = $.connection["dispatcher"];
			this.binarySelector = new BinarySelector("none");
			this.binarySelector.isSelecting = false;
			this.initialize();
		}

		private hookUpBinarySelection(eventObject): void {
			if (!GlobalViewModel.self.binarySelector.isSelecting) {
				return;
			}
			var binarySelector: BinarySelector = GlobalViewModel.self.binarySelector;

			var container: HTMLDivElement = <HTMLDivElement>eventObject.currentTarget;
			var index: number = $(container).index();
			$(container).addClass("selected");
			if (!binarySelector.firstAutomaton) {
				binarySelector.firstAutomaton = GlobalViewModel.self.automata()[index];
			}
			else if (!binarySelector.secondAutomaton) {
				binarySelector.secondAutomaton = GlobalViewModel.self.automata()[index];
				GlobalViewModel.self.binarySelector.isSelecting = false;
						
				GlobalViewModel.self.dispatcher.server[binarySelector.operation](binarySelector.firstAutomaton.toJson(), binarySelector.secondAutomaton.toJson())
					.done(function (automaton) {
						GlobalViewModel.self.createNewAutomaton(automaton);
						$(GlobalViewModel.automatonContainerName).removeClass("selected");
					})
					.fail(function () {
						console.log("Binary operation failed. Operation: " + binarySelector.operation);
						$(GlobalViewModel.automatonContainerName).removeClass("selected");
					});
			}
		}

		private initialize(): void {
			$(document).ready(function () {
				$(document).on("keyup input paste", GlobalViewModel.wordRecognizerFieldName, function (eventObject) {
					$(eventObject.currentTarget).removeClass("accepted rejected");
				});
				$(document).on("click", GlobalViewModel.automatonContainerName, GlobalViewModel.self.hookUpBinarySelection);
			});
		}

		private initSigma(viewModel: AutomatonViewModel, index: number): void {
			var visualizerContainer: HTMLElement = $(GlobalViewModel.automatonVisualizerName)[index];
			viewModel.initSigma(visualizerContainer);
		}

		public createNewAutomaton(automaton: any): void {
			var viewModel: AutomatonViewModel = new AutomatonViewModel(automaton);
			this.automata.push(viewModel);
			this.initSigma(viewModel, this.automata().length - 1);
		}

		private updateMachine(index: number, automaton: any): void {
			var viewModel = this.automata()[index];
			viewModel.update(automaton);
		}

		// TODO: UPDATE THE SERVER SIDE, TEST

		private sendUpdateRequest(index: number, operation: string): void {
			var automaton: AutomatonViewModel = this.automata()[index];
			this.dispatcher.server[operation](automaton.toJson())
				.done(function (json) {
					GlobalViewModel.self.updateMachine(index, json);
				})
				.fail(function () {
					console.log("Operation: " + operation + " failed!");
				});
		}

		public minimize(index: number): void {
			this.sendUpdateRequest(index, "minimize");
		}

		public determine(index: number): void {
			this.sendUpdateRequest(index, "determine");
		}

		public kleneeStar(index: number): void {
			this.sendUpdateRequest(index, "kleneeStar");
		}

		public negate(index: number): void {
			this.sendUpdateRequest(index, "negate");
		}

		public recognizeWord(index: number): void {
			var automaton: AutomatonViewModel = this.automata()[index];
			var inputField: HTMLInputElement = <HTMLInputElement> $(GlobalViewModel.wordRecognizerFieldName)[index]; 
			var word: string = inputField.value;
			this.dispatcher.server.recognizeWord(automaton.toJson(), word)
				.done(function (result) {
					if (result) {
						$(inputField).addClass("accepted");
					}
					else {
						$(inputField).addClass("rejected");
					}
				})
				.fail(function () {
					console.log("Could not recognize word. Automaton: " + index + ", Word: " + word);
				});
		}

		private binaryOperation(operation: string): void {
			this.binarySelector = new BinarySelector(operation);
		}

		public concatenate(): void {
			this.binaryOperation("concatenate");
		}

		public union(): void {
			this.binaryOperation("union");
		}

		public intersection(): void {
			this.binaryOperation("intersection");
		}

		public zoom(): void {
			this.isZoomedIn = !this.isZoomedIn;
			$(".automaton-container").toggleClass("zoom-out");
			$(".overlay").toggleClass("hidden");
		}
	}
}